package com.tinify.loader;

import com.size.check.SizeModel;
import com.tinify.AccountException;
import com.tinify.ClientException;
import com.tinify.ConnectionException;
import com.tinify.ServerException;
import com.tinify.Source;
import com.tinify.Tinify;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class TinifyApplication {

    private static volatile TinifyApplication instance = null;
    private ApplicationThread applicationThread;
    private final Set<File> pngFiles = new HashSet<>();
    private final List<String> tinifyKeys = new ArrayList<>();
    private float compressRatio = 0.1f;
    private List<SizeModel> warnningFiles = new ArrayList<>();
    private int counter = 0;

    public static TinifyApplication get() {
        if (instance == null) {
            synchronized (TinifyApplication.class) {
                if (instance == null) {
                    instance = new TinifyApplication();
                }
            }
        }
        return instance;
    }

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                TinifyApplication.get().onDestroy();
            }
        }));
    }

    private void onDestroy() {
        pngFiles.clear();
    }


    public static void main(String[] args) {
        TinifyApplication application = TinifyApplication.get();
        System.out.println("压缩任务开始");
        application.onCreate(args);
        application.onStart();
        System.out.println("压缩任务结束");
    }

    public void onCreate(String[] args) {
        applicationThread = new ApplicationThread();
        applicationThread.runMain(args);
    }

    public void onStart() {

        boolean hasInvalideKey = initTinifyKey();
        if (hasInvalideKey) {
            handleCompressTask();
            handleReusltInfo();
        } else {
            System.err.println("tinify api key使用次数不足");
        }

    }

    private void handleReusltInfo() {

        System.out.println("最终成功压缩文件："+counter+"个，总文件"+pngFiles.size()+"个");

        List<SizeModel> sizeModels = getWarnningFiles();
        if (sizeModels == null || sizeModels.size() <= 0) {
            return;
        }

        System.out.println("\r\n*-*-*-*-*发现无法压缩的超大文件，需要您手动处理*-*-*-*-*\r\n");
        for (SizeModel model : sizeModels) {
            System.out.println(model);
        }
        System.out.println("*-*-*-*-*-----*-*-*-*-*-----*-*-*-*-*\r\n");
    }

    private void handleCompressTask() {

        for (File pngFile : pngFiles) {
            try {
                String absolutePath = pngFile.getAbsolutePath();
                long srcLength = pngFile.length();

                System.out.println("开始压缩 " + absolutePath);
                System.out.println("原始尺寸 " + srcLength);

                Source source = Tinify.fromFile(absolutePath);
                byte[] bytes = source.toBuffer();

                if (bytes == null) continue;
                long finalLength = bytes.length;

                long dx = srcLength - finalLength;
                float ratio = (float) (dx * 1.0f / srcLength);
                System.out.println("最终尺寸: " + finalLength + " 压缩比例: " + (ratio * 100) + "%");
                if (ratio < compressRatio) {
                    if (srcLength > 20 * 1024) {
                        SizeModel sm = new SizeModel();
                        sm.path = absolutePath;
                        sm.size = srcLength;
                        warnningFiles.add(sm);
                    }
                    System.err.println("压缩比例低于" + (compressRatio * 100) + "%，无需更新本地图片\r\n");
                    continue;
                }
                writeToFile(absolutePath, bytes);
                System.out.println("更新图片到：" + absolutePath + "\r\n");
                counter++;
            } catch (AccountException e) {
                e.printStackTrace();
            } catch (ClientException e) {
                e.printStackTrace();
            } catch (ServerException e) {
                e.printStackTrace();
            } catch (ConnectionException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                int count = Tinify.compressionCount();
                if (count > 0) {
                    continue;
                }
                boolean hasInvalidKey = initTinifyKey();
                if (!hasInvalidKey) {
                    System.err.println("tinify api key使用次数不足");
                    break;
                }
            }
        }
    }

    public List<SizeModel> getWarnningFiles() {
        return warnningFiles;
    }

    private void writeToFile(String absolutePath, byte[] bytes) {
        if (bytes == null || absolutePath == null) return;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(absolutePath);
            fos.write(bytes, 0, bytes.length);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private boolean initTinifyKey() {

        if (tinifyKeys.size() == 0) return false;
        while (tinifyKeys.size() > 0) {

            Random random = new Random(System.currentTimeMillis());
            int rndIntVal = random.nextInt(10000);
            int index = rndIntVal % tinifyKeys.size();
            String key = tinifyKeys.get(index);
            try {
                Tinify.setKey(key);
                Tinify.validate();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                tinifyKeys.remove(key);
            }
        }
        return false;
    }

    public void addFile(File adsFile) {
        pngFiles.add(adsFile);
    }

    public void setTinifyKeys(String[] keys) {

        if (keys == null) return;
        for (String key : keys) {
            tinifyKeys.add(key);
        }

    }

    public void setRatio(float v) {
        this.compressRatio = v;
    }
}
